#pragma once
#include <string>
#include <windows.h>
#include "../Utils/PlatTypeUtil.h"
#include <functional>
#include "../Game/Scene/GameScene.h"
#include "../Game/Process/RenderProcess.h"
#include "../Templates/NewObject.h"

class MaggotApp
{
public:

	static MaggotApp& Get();
	~MaggotApp();

	MaggotApp(const MaggotApp& rhs) = delete;
	MaggotApp(MaggotApp&& rhs) = delete;
	MaggotApp& operator=(const MaggotApp& rhs) = delete;
	MaggotApp& operator=(MaggotApp&& rhs) = delete;

	void SetWindowTitle(const TCHAR* title) { mWindowTitle = title; }
	void SetWindowSize(uint32 width, uint32 height) { mWidth = width; mHeight = height; }
	uint32 GetWidth() const { return mWidth; }
	uint32 GetHeight() const { return mHeight; }
	void ResizeWindow(uint32 width, uint32 height);

	template<typename T, typename...Args>
	void SetGameScene(Args&&... args);

	GameScene* CreateGameScene();
	void DeleteGameScene(GameScene* gameScene);

	template<typename T, typename...Args>
	void AddRenderProcess(Args&&... args);

	std::vector<RenderProcess*> CreateRenderProcess();
	void DeleteRenderProcess(std::vector<RenderProcess*>& renderProcessMap);

	void Run();

private:

	MaggotApp();

	class Window
	{
		friend class MaggotApp;
	public:
		Window(MaggotApp* maggotApp, std::wstring title, uint32 width, uint32 height);
		virtual ~Window();

		HWND GetWindowId() const { return mHwnd; }

	private:

		static LRESULT WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

	private:

		MaggotApp* mMaggotApp;
		HWND mHwnd;
		HINSTANCE mhInstance;

		std::wstring mWindowTitle;
	};

public:

	Window& GetWindow() const { return *mWindow; }

private:

	std::wstring mWindowTitle;
	uint32 mWidth;
	uint32 mHeight;
	Window* mWindow;

	std::function<GameScene* ()> mCreateGameScene;
	std::function<void(GameScene*)> mDeleteGameScene;

	std::vector<std::function<RenderProcess* ()>> mCreateRenderProcess;
	std::vector<std::function<void(RenderProcess*)>> mDeleteRenderProcess;
};

template<typename T, typename...Args>
void MaggotApp::SetGameScene(Args&&... args)
{
	mCreateGameScene = [&]() {
		return NewObject<T>(std::forward<Args>(args)...);
	};
	mDeleteGameScene = [](GameScene* gameScene) {
		DeleteObject<T>(static_cast<T*>(gameScene));
	};
}

template<typename T, typename...Args>
void MaggotApp::AddRenderProcess(Args&&... args)
{
	auto createFunc = [&]() {
		return NewObject<T>(std::forward<Args>(args)...);
	};
	mCreateRenderProcess.push_back(std::move(createFunc));

	auto deleteFunc = [](RenderProcess* renderProcess) {
		DeleteObject<T>(static_cast<T*>(renderProcess));
	};
	mDeleteRenderProcess.push_back(deleteFunc);
}

